﻿using System;
using System.Linq;

namespace CommonUtils
{
    /// <summary>
    /// 时间相关工具
    /// </summary>
    public static class TimeUtil
    {
        /// <summary>
        /// 网络时间,失败返回 DateTime.Now
        /// </summary>
        private static DateTime GetNet()
        {
            try
            {
                var res = HttpUtil.Post("https://www.baidu.com/");
                if (DateTime.TryParse(res.Headers.GetValues("Date").FirstOrDefault(), out DateTime time))
                    return time;

                LogUtil.Write("网络时间获取失败: " + res);
                return DateTime.Now;
            }
            catch (Exception ex)
            {
                LogUtil.Write("网络时间获取失败: " + ex.Message);
                return DateTime.Now;
            }
        }

        /// <summary>
        /// 时间差
        /// </summary>
        private static long _netDiff { get; set; }

        /// <summary>
        /// 刷新网络时间，一般在程序更改时间后调用
        /// </summary>
        public static void NetRefresh()
        => _netDiff = (GetNet() - DateTime.Now).Ticks;

        /// <summary>
        /// 网络时间
        /// </summary>
        public static DateTime Net
        {
            get
            {
                if (_netDiff == 0)
                    NetRefresh();
                return DateTime.Now.AddTicks(_netDiff);
            }
        }

        /// <summary>
        /// 时间戳起点
        /// </summary>
        private static DateTime _timestampStart { get; } = DateTime.Parse("1970-01-01 00:00:00") + TimeZoneInfo.Local.BaseUtcOffset;

        /// <summary>
        /// 从时间戳获取时间
        /// </summary> 
        public static DateTime FromStamp(long stamp)
        {
            while (stamp < 1000000000000)
                stamp *= 10;
            return _timestampStart.AddMilliseconds(stamp % 10000000000000);
        }

        /// <summary>
        /// 时间戳 10位 秒级
        /// </summary> 
        public static int GetStamp(this DateTime time)
            => (int)(time - _timestampStart).TotalSeconds;

        /// <summary>
        /// 时间戳 13位 毫秒级
        /// </summary> 
        public static long GetLongStamp(this DateTime time)
        => (long)(time - _timestampStart).TotalMilliseconds;
        public static long GetLongStamp()
        => DateTime.Now.GetLongStamp();

        /// <summary>
        /// 日期时间戳 10位 秒级
        /// </summary>
        public static int GetDateStamp(this DateTime time)
        => time.Date.GetStamp();

        /// <summary>
        /// 月份时间戳 10位 秒级
        /// </summary>
        public static int MonthStamp(this DateTime time)
        => time.AddDays(1 - time.Day).GetDateStamp();

        /// <summary>
        /// 仅日期时间戳
        /// </summary>
        public static long GetOnlyDateLongStamp(this DateTime time)
        => time.Date.GetLongStamp();

        /// <summary>
        /// 获取时间
        /// </summary> 
        public static DateTime FromTicks(long ticks)
        => new DateTime(ticks);

        /// <summary>
        /// 获取时间[yyyy-MM-dd HH:mm:ss]
        /// </summary> 
        public static DateTime Parse(object time)
        {
            try
            {
                return DateTime.Parse(time.ToString());
            }
            catch
            {
                LogUtil.Print("Try to parse to time error with: [{0}]", time);
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 获取时间[Wed Sep 29 10:02:41 2021 +0800]
        /// </summary>
        public static DateTime ParseExact(string time)
        {
            try
            {
                var cells = time.Split(' ');
                int year = cells[4].ToInt();

                int month = 0;
                switch (cells[1])
                {
                    case "一月":
                    case "January":
                    case "Jan":
                        month = 1;
                        break;

                    case "二月":
                    case "February":
                    case "Feb":
                        month = 2;
                        break;

                    case "三月":
                    case "March":
                    case "Mar":
                        month = 3;
                        break;

                    case "四月":
                    case "April":
                    case "Apr":
                        month = 4;
                        break;

                    case "五月":
                    case "May":
                        month = 5;
                        break;

                    case "六月":
                    case "June":
                    case "Jun":
                        month = 6;
                        break;

                    case "七月":
                    case "July":
                    case "Jul":
                        month = 7;
                        break;

                    case "八月":
                    case "August":
                    case "Aug":
                        month = 8;
                        break;

                    case "九月":
                    case "September":
                    case "Sep":
                    case "Sept":
                        month = 9;
                        break;

                    case "十月":
                    case "October":
                    case "Oct":
                        month = 10;
                        break;

                    case "十一月":
                    case "November":
                    case "Nov":
                        month = 11;
                        break;

                    case "十二月":
                    case "December":
                    case "Dec":
                        month = 12;
                        break;
                }

                int day = cells[2].ToInt();
                var subCells = cells[3].Split(':');
                int hour = subCells[0].ToInt();
                int minute = subCells[1].ToInt();
                int second = subCells[2].ToInt();
                return new DateTime(year, month, day, hour, minute, second);
            }
            catch (Exception ex)
            {
                LogUtil.Print("Try to parse to time error with: [{0}]\r\n{1}", time, ex.Message);
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 获取时间
        /// </summary> 
        public static DateTime ToTime(this object time)
        => Parse(time);

        /// <summary>
        /// 非标准时间字符串转为时间,标准时间字符串直接使用Parse或ToDateTime
        /// </summary>
        public static DateTime FromFormat(string str, string format = "yyyyMMddHHmmssfff")
        {
            try
            {
                //截取相同长度
                if (str.Length < format.Length)
                    format = format.Substring(0, str.Length);
                if (str.Length > format.Length)
                    str = str.Substring(0, format.Length);
                //转换
                return DateTime.ParseExact(str, format, null);
            }
            catch
            {
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetYearString(this DateTime time)
        => time.ToString("yyyy");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetMonthString(this DateTime time)
        => time.ToString("MM");

        /// <summary>
        /// 获取星期信息
        /// </summary>
        public static string WeekString(this DateTime time)
        => time.ToString("dddd");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetDayString(this DateTime time)
        => time.ToString("dd");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetHourString(this DateTime time)
        => time.ToString("HH");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetMinuteString(this DateTime time)
        => time.ToString("mm");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetSecondString(this DateTime time)
        => time.ToString("ss");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string GetDateString(this DateTime time)
        => time.ToString("yyyy-MM-dd");

        /// <summary>
        /// 时间拆分字符串
        /// </summary>
        public static string TimeString(this DateTime time)
        => time.ToString("HH:mm:ss");

        /// <summary>
        /// 精确到毫秒 yyyy-MM-ddTHH:mm:ss.fff"
        /// </summary>
        public static string FullInfo(this DateTime time)
        => time.ToString("yyyy-MM-dd HH:mm:ss.fff");

        /// <summary>
        /// 时间字符串
        /// </summary>
        public static string Info(this DateTime time, string format = "yyyy-MM-dd HH:mm:ss")
        => time.ToString(format);

        /// <summary>
        /// 带小数的小时
        /// </summary>
        public static float FloatHour(this DateTime time)
        => time.Hour + (float)time.Minute / 60;

        /// <summary>
        /// 不计日期 只计时间
        /// </summary>
        public static DateTime Time(this DateTime time)
        => new DateTime().Add(time - time.Date);

        /// <summary>
        /// 时间计算的中间状态
        /// </summary>
        public static TimeSpan Span(this DateTime time)
        => time - new DateTime();

        /// <summary>
        /// 时间累加
        /// </summary>
        public static DateTime Add(this DateTime time1, DateTime time2)
        => time1.AddTicks(time2.Ticks);

        /// <summary>
        /// 时间累加,仅仅时间
        /// </summary>
        public static DateTime AddTime(this DateTime time1, DateTime time2)
        => time1.AddTicks(time2.Time().Ticks);

        /// <summary>
        /// 替换日期
        /// </summary>
        public static DateTime ReplaceDate(this DateTime time, DateTime dateTime)
        => dateTime.Date.AddTime(time.Time());

        /// <summary>
        /// 返回当前的分钟序列，一天有60*24个序列
        /// </summary>
        public static int MinuteIndex(this DateTime time)
        => (int)time.Time().Span().TotalMinutes;

        /// <summary>
        /// 格林威治时间
        /// </summary>
        public static string ToGmt(this DateTime time)
        => time.ToUniversalTime().ToString("r");

        /// <summary>
        /// 格林威治时间
        /// </summary>
        public static DateTime FromGmt(string gmt)
        => DateTime.Parse(gmt);

        public static DateTime Now
        => DateTime.Now;

        public static int NowStamp
        => Now.GetStamp();

        public static long NowLongStamp
        => Now.GetLongStamp();
    }
}